package com.neusoft.neuiotms.dcom.service.malfunction.impl;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.neusoft.bizcore.activiti.dto.WorkorderDTO;
import com.neusoft.bizcore.activiti.enums.WorkOrderStatus;
import com.neusoft.bizcore.activiti.model.WorkOrder;
import com.neusoft.bizcore.activiti.repository.WorkOrderRepository;
import com.neusoft.bizcore.activiti.service.WorkOrderService;
import com.neusoft.bizcore.auth.common.bean.RoleBean;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.web.dto.result.ResultDTO;
import com.neusoft.bizcore.web.dto.result.ResultListDTO;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.bizcore.web.utils.EasyPoiStylerColorImpl;
import com.neusoft.neuiotms.dcom.constants.DCOMConstants;
import com.neusoft.neuiotms.dcom.dto.InstitutionsDTO;
import com.neusoft.neuiotms.dcom.dto.ProjectDTO;
import com.neusoft.neuiotms.dcom.model.malfunction.MalfunctionApplyForm;
import com.neusoft.neuiotms.dcom.model.malfunction.MalfunctionFrontlineProcessingForm;
import com.neusoft.neuiotms.dcom.model.malfunction.MalfunctionSecondLine;
import com.neusoft.neuiotms.dcom.repository.malfunction.MalfunctionApplyFormRepository;
import com.neusoft.neuiotms.dcom.repository.malfunction.MalfunctionFrontlineProcessingFormRepository;
import com.neusoft.neuiotms.dcom.repository.malfunction.MalfunctionSecondLineRepository;
import com.neusoft.neuiotms.dcom.service.InstitutionsService;
import com.neusoft.neuiotms.dcom.service.ProjectService;
import com.neusoft.neuiotms.dcom.service.malfunction.MalfunctionService;
import com.neusoft.neuiotms.dcom.util.PermUtils;
import com.neusoft.neuiotms.micro.auth.AuthMicroService;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.word.WordExportUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class MalfunctionServiceImpl implements MalfunctionService {

    @Autowired
    private WorkOrderService workOrderService;
    @Autowired
    private WorkOrderRepository workOrderRepository;
    @Autowired
    private MalfunctionSecondLineRepository secondLineRepository;
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private MalfunctionApplyFormRepository malfunctionApplyFormRepository;
    @Autowired
    private MalfunctionFrontlineProcessingFormRepository malfunctionFrontlineProcessingFormRepository;
    @Autowired
    private AuthMicroService authMicroService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private InstitutionsService institutionsService;

    @Override
    public int auditHandleMalfunctionNumByUser(final UserBean user) {

        final List<WorkOrder> doneWorkOrders =
                this.workOrderRepository.findByTypeAndStatus("malfunction", WorkOrderStatus.finish);

        if (null == doneWorkOrders) {
            return 0;
        }

        if (PermUtils.isAdmin(user) || PermUtils.isZongjian(user)) {
            return doneWorkOrders.size();
        }

        final List<UserBean> users = Lists.newArrayList();
        if (PermUtils.isWorkerGroupLeader(user)) {
            final List<InstitutionsDTO> insts = this.institutionsService.getPermInstitutions(user);
            insts.stream().forEach(it -> {
                it.getResidents().stream().forEach(r -> {
                    final UserBean bean = new UserBean();
                    bean.setUsername(r.getUsername());
                    final RoleBean role = new RoleBean("worker", null);
                    bean.setRoles(Lists.newArrayList(role));
                    users.add(bean);
                });
            });
        } else if (PermUtils.isWorker(user)) {
            users.add(user);
        }

        int total = 0;
        for (final UserBean bean : users) {
            final Searchable searchable = new Searchable();
            searchable.put("type", "malfunction");
            final Pageable pageable = PageRequest.of(0, 100000);
            final Page<WorkorderDTO> result = this.workOrderService.doneWorkSearch(pageable, searchable, bean);
            final List<WorkorderDTO> myWorkOrders = result.getContent();
            final List<WorkorderDTO> crossWorkOrders = myWorkOrders.stream()
                    .filter(my -> doneWorkOrders.stream().filter(done -> done.getId().equals(my.getId())).findAny()
                            .isPresent())
                    .collect(Collectors.toList());
            total += crossWorkOrders.size();
        }

        return total;
    }

    @Override
    public int auditCoSponsorMalfunctionNumByUser(final UserBean user) {
        final List<MalfunctionSecondLine> list = this.secondLineRepository.findAll();
        final List<String> workOrder = new ArrayList<>();
        list.stream().forEach(l -> {
            final String engineerCode = l.getEngineerCode();
            if (StringUtils.isNotEmpty(engineerCode)) {
                final List<String> codes = Stream.of(engineerCode.split(",")).collect(Collectors.toList());
                if (codes.contains(user.getUsername())) {
                    workOrder.add(l.getWorkNum());
                }

            }
        });

        return workOrder.stream().distinct().collect(Collectors.toList()).size();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Page<Map<String, Object>> report(Pageable pageable, final Map<String, Object> params) {

        final String countSql = this.buildTotalSql(params);
        MalfunctionServiceImpl.log.info(countSql);
        final Query query1 = this.entityManager.createNativeQuery(countSql);
        query1.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result1 = query1.getResultList();
        int total = 0;
        if (result1.size() > 0) {
            total = Integer.parseInt(String.valueOf(result1.get(0).get("count")));
        }
        final String sql = this.buildQuerySql(pageable, params);
        MalfunctionServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();
        return new PageImpl<>(result, pageable, total);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Map<String, Object>> report(final Map<String, Object> params) {
        final String sql = this.buildExportSql(params);
        MalfunctionServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();
        return result;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Workbook exportReport(final Map<String, Object> params) {
        final String sql = this.buildExportSql(params);
        MalfunctionServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();

        final List<ReportVO> list = new ArrayList<>(result.size() == 0 ? 1 : result.size());
        result.stream().map(t -> {
            final ReportVO vo = new ReportVO();
            vo.setService_unit_name(t.get("service_unit_name"));
            vo.setA01_count(t.get("a01_count"));
            vo.setA02_count(t.get("a02_count"));
            vo.setA03_count(t.get("a03_count"));
            vo.setA04_count(t.get("a04_count"));
            vo.setA05_count(t.get("a05_count"));
            vo.setA06_count(t.get("a06_count"));
            vo.setA07_count(t.get("a07_count"));
            vo.setA08_count(t.get("a08_count"));
            vo.setA09_count(t.get("a09_count"));
            return vo;
        }).forEach(list::add);

        final ExportParams param =
                new ExportParams("故障服务统计", "故障服务统计", ExcelType.XSSF);
        param.setStyle(EasyPoiStylerColorImpl.class);
        return ExcelExportUtil.exportExcel(param, ReportVO.class, list);
    }

    private String buildTotalSql(final Map<String, Object> params) {
        final String statement = " count(*) as count ";
        return this.buildSql(params, statement);
    }

    private String buildQuerySql(Pageable pageable, final Map<String, Object> params) {
        final String statement = " * ";
        String noPageSql = this.buildSql(params, statement);
        noPageSql += " limit " + pageable.getPageSize() + " offset " + pageable.getOffset();

        return noPageSql;
    }

    private String buildExportSql(final Map<String, Object> params) {
        final String statement = " * ";
        return this.buildSql(params, statement);
    }

    private String buildSql(final Map<String, Object> params, String statement) {
        String where = " ";
        if (params.containsKey("service_unit_code")) {
            where += "and t1.service_unit_code='" + (String) params.get("service_unit_code") + "' ";
        }
        if (params.containsKey("start")) {
            where += "and t0.update_time>='" + (String) params.get("start") + "' ";
        }
        if (params.containsKey("end")) {
            Date end = null;
            try {
                end = DateUtils.parseDate((String) params.get("end"), "yyyy-MM-dd");
            } catch (final ParseException e) {
            }
            if (null != end) {
                final Calendar c = Calendar.getInstance();
                c.setTime(end);
                c.add(Calendar.DAY_OF_MONTH, 1);
                where += "and t0.update_time<'" + DateFormatUtils.format(c.getTime(), "yyyy-MM-dd") + "' ";
            }
        }

        String sql = "SELECT "
                + statement
                + " FROM ( "
                + "SELECT "
                + " service_unit_code, "
                + " service_unit_name, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A01' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a01_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A02' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a02_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A03' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a03_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A04' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a04_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A05' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a05_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A06' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a06_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A07' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a07_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A08' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a08_count, "
                + " max( "
                + "     CASE worktype_code "
                + "     WHEN 'A09' THEN "
                + "         count "
                + "     ELSE "
                + "         0 "
                + "     END "
                + " ) a09_count "
                + "FROM "
                + " ( "
                + "     SELECT "
                + "         t1.service_unit_code, "
                + "         t1.service_unit_name, "
                + "         t2.worktype_code, "
                + "         t2.worktype_name, "
                + "         count(1) AS count "
                + "     FROM "
                + "         wf_work_order t0 "
                + "     LEFT JOIN neuiot_malfunction_apply_form t1 ON t0.work_order_num = t1.work_num "
                + "     LEFT JOIN neuiot_malfunction_frontline_processing_form t2 ON t0.work_order_num = t2.work_num "
                + "     WHERE "
                + "         t0.type = 'malfunction' "
                + "         and t0.STATUS = 'finish' "
                + "         %s "
                + "     GROUP BY "
                + "         t1.service_unit_code, "
                + "         t1.service_unit_name, "
                + "         t2.worktype_code, "
                + "         t2.worktype_name "
                + " ) t "
                + "GROUP BY "
                + " service_unit_code, "
                + " service_unit_name "
                + ") t";

        sql = sql.replace("%s", where);

        return sql;
    }

    @Setter
    @Getter
    static class ReportVO {

        @Excel(name = "服务厅局", orderNum = "0", width = 30)
        private Object service_unit_name;

        @Excel(name = "机房及配套设施类", orderNum = "1", width = 30)
        private Object a01_count;

        @Excel(name = "网络类", orderNum = "2", width = 30)
        private Object a02_count;

        @Excel(name = "安全类", orderNum = "3", width = 30)
        private Object a03_count;

        @Excel(name = "服务器类", orderNum = "4", width = 30)
        private Object a04_count;

        @Excel(name = "存储设备类", orderNum = "5", width = 30)
        private Object a05_count;

        @Excel(name = "资产管理类", orderNum = "6", width = 30)
        private Object a06_count;

        @Excel(name = "文档类", orderNum = "7", width = 30)
        private Object a07_count;

        @Excel(name = "其他辅助类", orderNum = "8", width = 30)
        private Object a08_count;

        @Excel(name = "服务扩展类", orderNum = "9", width = 30)
        private Object a09_count;

    }

    @Override
    public XWPFDocument export(final String worknum) {
        final Map<String, Object> map = new HashMap<>();
        if (worknum == null) {
            throw new UnitedException("导出故障工单出错，必须选择一张单据");
        }

        final ProjectDTO project = this.projectService.getInfoByCode("HN");
        // 项目名称
        map.put("project", project.getName());

        final MalfunctionApplyForm applyForm =
                this.malfunctionApplyFormRepository.findTopByWorkNumOrderByIdDesc(worknum);
        final Calendar calendar = Calendar.getInstance();

        if (null != applyForm) {
            // 服务单号 caseNo
            map.put("caseNo", applyForm.getWorkNum());
            // 客户姓名
            map.put("customerName", applyForm.getCustomerName());
            // 客户电话
            map.put("customerPhone", applyForm.getCustomerPhone());
            // 处室
            map.put("place", applyForm.getPlace());
            // 房间号
            map.put("roomNumber", applyForm.getRoomNumber());
            // 请求时间 格式 年-月-日-时-分
            calendar.setTime(applyForm.getCreateTime());
            map.put("requestTime",
                    calendar.get(Calendar.YEAR) + "年" + (calendar.get(Calendar.MONTH) + 1) + "月"
                            + calendar.get(Calendar.DAY_OF_MONTH) + "日" + calendar.get(Calendar.HOUR_OF_DAY) + "时"
                            + calendar.get(Calendar.MINUTE) + "分");
            // 是否涉及涉密数据/设备
            map.put("whetherSecret", applyForm.getWhetherSecret() ? "是" : "否");
            map.put("whetherBackup", applyForm.getWhetherBackup() ? "是" : "否");
            // 服务需求或故障现象
            map.put("demandOrPhenomenon", applyForm.getEventDescription());
            map.put("engineer", applyForm.getFrontlineEngineerName());
            final ResultDTO<UserBean> engineerUserBean =
                    this.authMicroService.loadUserByUsername(applyForm.getFrontlineEngineerCode());
            if ((null != engineerUserBean) && (null != engineerUserBean.getData())) {
                map.put("phone", engineerUserBean.getData().getPhone());
            }
        }

        final MalfunctionFrontlineProcessingForm frontlineProcessingForm =
                this.malfunctionFrontlineProcessingFormRepository.findTopByWorkNumOrderByIdDesc(worknum);

        // 已进行的处理动作
        if (null != frontlineProcessingForm) {
            calendar.setTime(frontlineProcessingForm.getCreateTime());
            map.put("startTime", DateFormatUtils.format(calendar, "yyyy-MM-dd HH:mm"));
            map.put("problem", frontlineProcessingForm.getFailureSolveSituation());
            map.put("serviceType", frontlineProcessingForm.getWorktypeName());
        } else {
            map.put("problem", "");
        }

        // 先判断流程是否处于一线工程师导入客户评价和反馈节点，如果是，停止催单
        final WorkorderDTO workForm = this.workOrderService.details(worknum);
        final String instanceId = workForm.getInstanceId();
        final List<Task> tasks = this.taskService.createTaskQuery().processInstanceId(instanceId).list();
        if (tasks.stream().filter(it -> "MALFUNCTIONCUSTOMERREVIEWFORM".equals(it.getFormKey())).findAny()
                .isPresent()) {
            calendar.setTime(workForm.getUpdateTime());
            map.put("endTime", DateFormatUtils.format(calendar, "yyyy-MM-dd HH:mm"));
        }

        // 处理结果及存在问题
        //map.put("problem", "");

        try {
            final XWPFDocument doc = WordExportUtil.exportWord07(
                    "word/malfunction.docx", map);
            return doc;
        } catch (final Exception e) {
            throw new UnitedException("导出故障工单出错");
        }
    }

    @Override
    public long countOfFinishedMalfunction(UserBean user) {
        if (PermUtils.isAdmin(user) || PermUtils.isZongjian(user) || PermUtils.isBackup(user)) {
            final List<WorkOrder> doneWorkOrders =
                    this.workOrderRepository.findByTypeAndStatus("malfunction", WorkOrderStatus.finish);
            return doneWorkOrders.size();
        } else if (PermUtils.isWorker(user)) {
            final List<MalfunctionApplyForm> list =
                    this.malfunctionApplyFormRepository.findByFrontlineEngineerCode(user.getUsername());

            final List<String> workNumArr =
                    list.stream().map(it -> it.getWorkNum()).distinct().collect(Collectors.toList());

            return workNumArr.stream()
                    .filter(workNum -> WorkOrderStatus.finish
                            .equals(this.workOrderRepository.findByWorkOrderNum(workNum).getStatus()))
                    .count();
        } else if (PermUtils.isWorkerGroupLeader(user)) {
            final List<InstitutionsDTO> myInsts = this.institutionsService.getPermInstitutions(user);

            final List<MalfunctionApplyForm> list = this.malfunctionApplyFormRepository
                    .findByServiceUnitCodeIn(myInsts.stream().map(it -> it.getCode()).collect(Collectors.toList()));

            final List<String> workNumArr =
                    list.stream().map(it -> it.getWorkNum()).distinct().collect(Collectors.toList());

            return workNumArr.stream()
                    .filter(workNum -> WorkOrderStatus.finish
                            .equals(this.workOrderRepository.findByWorkOrderNum(workNum).getStatus()))
                    .count();
        } else if (PermUtils.isExport(user)) {
            final List<MalfunctionSecondLine> list = this.secondLineRepository.findByOperatorCodeInOrTransferUserNameIn(
                    Lists.newArrayList(user.getUsername()), Lists.newArrayList(user.getUsername()));

            final List<String> workNumArr =
                    list.stream().map(it -> it.getWorkNum()).distinct().collect(Collectors.toList());

            return workNumArr.stream()
                    .filter(workNum -> WorkOrderStatus.finish
                            .equals(this.workOrderRepository.findByWorkOrderNum(workNum).getStatus()))
                    .count();
        } else if (PermUtils.isExpertGroupLeader(user)) {
            final Map<String, Object> query = Maps.newHashMap();
            query.put("s_roleCode", DCOMConstants.ROLE_EXPERT);
            final ResultListDTO<Map<String, Object>> result = this.authMicroService.allUsers(query);
            final List<Map<String, Object>> exports = result.getData();
            final List<String> usernameArr =
                    exports.stream().map(map -> (String) map.get("username")).collect(Collectors.toList());

            final List<MalfunctionSecondLine> list = this.secondLineRepository.findByOperatorCodeInOrTransferUserNameIn(
                    usernameArr, usernameArr);
            final List<String> workNumArr =
                    list.stream().map(it -> it.getWorkNum()).distinct().collect(Collectors.toList());
            return workNumArr.stream()
                    .filter(workNum -> WorkOrderStatus.finish
                            .equals(this.workOrderRepository.findByWorkOrderNum(workNum).getStatus()))
                    .count();
        }
        return 0;
    }

}
